#ifndef OPTFRAME_NSSEQPERIODICSWAP2_2_HPP_
#define OPTFRAME_NSSEQPERIODICSWAP2_2_HPP_

// Framework includes
#include "../OptFrame/Move.hpp"
#include "../OptFrame/NSSeq.hpp"

using namespace std;

// Working structure: vector<T>

class MovePeriodicSwap2_2: public Move<RepPRVP, MemPRVP>
{

protected:
	int p; // period changed
	int r1, r2; // route 1 and route 2;
	int c1, c2; // position 1 and position 2 of the clients, respectively
	bool reverseR1, reverseR2; // true -> reverse movement of route R

public:

	MovePeriodicSwap2_2(int _p, int _r1, int _r2, int _c1, int _c2, bool _reverseR1, bool _reverseR2) :
		p(_p), r1(_r1), r2(_r2), c1(_c1), c2(_c2), reverseR1(_reverseR1), reverseR2(_reverseR2)
	{
	}

	virtual ~MovePeriodicSwap2_2()
	{
	}

	int get_p()
	{
		return p;
	}

	bool get_reverseR1()
	{
		return reverseR1;
	}

	bool get_reverseR2()
	{
		return reverseR2;
	}

	int get_r1()
	{
		return r1;
	}

	int get_r2()
	{
		return r2;
	}

	int get_c1()
	{
		return c1;
	}

	int get_c2()
	{
		return c2;
	}

	bool canBeApplied(const RepPRVP& rep)
	{
		bool all_positive = (r1 >= 0) && (r2 >= 0) && (c1 >= 0) && (c2 >= 0) && (p >= 0);
		return all_positive && (rep.at(p).size() >= 2) && (rep.at(p).at(r1).size() >= 2) && (rep.at(p).at(r2).size() >= 2);
	}

	virtual MovePeriodicSwap2_2& apply(RepPRVP& rep)
	{
		int aux;

		if (reverseR1)
		{
			aux = rep.at(p).at(r1).at(c1);
			rep.at(p).at(r1).at(c1) = rep.at(p).at(r1).at(c1 + 1);
			rep.at(p).at(r1).at(c1 + 1) = aux;
		}

		if (reverseR2)
		{
			aux = rep.at(p).at(r2).at(c2);
			rep.at(p).at(r2).at(c2) = rep.at(p).at(r2).at(c2 + 1);
			rep.at(p).at(r2).at(c2 + 1) = aux;
		}

		//swapping
		aux = rep.at(p).at(r1).at(c1);
		rep.at(p).at(r1).at(c1) = rep.at(p).at(r2).at(c2);
		rep.at(p).at(r2).at(c2) = aux;

		aux = rep.at(p).at(r1).at(c1 + 1);
		rep.at(p).at(r1).at(c1 + 1) = rep.at(p).at(r2).at(c2 + 1);
		rep.at(p).at(r2).at(c2 + 1) = aux;

		// p->p, r1->r2, r2->r1, c1->c2, c2->c1, reverse->reverse,
		return *new MovePeriodicSwap2_2(p, r2, r1, c2, c1, reverseR1, reverseR2);
	}

	virtual bool operator==(const Move<RepPRVP, MemPRVP>& _m) const
	{
		const MovePeriodicSwap2_2& m1 = (const MovePeriodicSwap2_2&) _m;
		return ((m1.p == p) && (m1.r1 == r1) && (m1.r2 == r2) && (m1.c1 == c1) && (m1.c2 == c2) && (m1.reverseR1 == reverseR1) && (m1.reverseR2 == reverseR2));
	}

	void print() const
	{
		cout << "MovePeriodicSwap2_2( ";
		cout << p << ", " << r1 << ", " << r2 << ", " << c1 << ", " << c2 << ", " << reverseR1 << ", " << reverseR2 << " )";
		cout << endl;
	}
};

class NSIteratorPeriodicSwap2_2: public NSIterator<RepPRVP, MemPRVP>
{

private:

	MovePeriodicSwap2_2* m;
	vector<MovePeriodicSwap2_2*> moves;
	int index; //index of moves
	const RepPRVP& r;

public:

	NSIteratorPeriodicSwap2_2(const RepPRVP& _r) :
		r(_r)
	{
		m = NULL;
		index = 0;
	}

	virtual ~NSIteratorPeriodicSwap2_2()
	{
		for (int i = 0; i < moves.size(); i++)
			delete moves[i];
	}

	void first()
	{

		for (int p = 0; p < r.size(); p++)
		{
			for (int r1 = 0; r1 < r.at(p).size() - 1; r1++)
			{
				if (r.at(p).at(r1).size() >= 2) //verificando se há pelo menos dois clientes.
				{
					for (int r2 = r1 + 1; r2 < r.at(p).size(); r2++)
					{
						if (r.at(p).at(r2).size() >= 2) //verificando se há pelo menos dois clientes.
						{
							for (int c1 = 0; c1 < r.at(p).at(r1).size() - 1; c1++)
							{
								for (int c2 = 0; c2 < r.at(p).at(r2).size() - 1; c2++)
								{
									moves.push_back(new MovePeriodicSwap2_2(p, r1, r2, c1, c2, false, false));//normal
									moves.push_back(new MovePeriodicSwap2_2(p, r1, r2, c1, c2, true, false));//reverse route 1
									moves.push_back(new MovePeriodicSwap2_2(p, r1, r2, c1, c2, false, true));//reverse route 2
									moves.push_back(new MovePeriodicSwap2_2(p, r1, r2, c1, c2, true, true));//reverse booth
								}
							}
						}
					}
				}
			}
		}

		if (moves.size() > 0)
		{
			m = moves[index];
		}
		else
			m = NULL;
	}

	void next()
	{
		index++;
		if (index < moves.size())
		{
			m = moves[index];
		}
		else
			m = NULL;
	}

	bool isDone()
	{
		return m == NULL;
	}

	MovePeriodicSwap2_2& current()
	{
		if (isDone())
		{
			cout << "There isnt any current element!" << endl;
			cout << "NSSeqPeriodicSwap2_2. Aborting." << endl;
			exit(1);
		}

		return *m;
	}
};

class NSSeqPeriodicSwap2_2: public NSSeq<RepPRVP, MemPRVP>
{

private:

public:

	NSSeqPeriodicSwap2_2()
	{
	}

	virtual ~NSSeqPeriodicSwap2_2()
	{
	}

	MovePeriodicSwap2_2& move(const RepPRVP& rep)
	{
		int p = rand() % rep.size();
		if (rep.at(p).size() < 2)
			return *new MovePeriodicSwap2_2(-1, -1, -1, -1, -1, false, false);

		int r1 = rand() % rep.at(p).size();

		if (rep.at(p).at(r1).size() < 2)
			return *new MovePeriodicSwap2_2(-1, -1, -1, -1, -1, false, false);

		int r2;

		do
		{
			r2 = rand() % rep.at(p).size();
		} while (r1 == r2);

		if (rep.at(p).at(r2).size() < 2)
			return *new MovePeriodicSwap2_2(-1, -1, -1, -1, -1, false, false);

		int c1 = rand() % (rep.at(p).at(r1).size() - 1);
		int c2 = rand() % (rep.at(p).at(r2).size() - 1);

		bool reverseR1 = rand() % 2;
		bool reverseR2 = rand() % 2;

		return *new MovePeriodicSwap2_2(p, r1, r2, c1, c2, reverseR1, reverseR2);
	}

	virtual NSIteratorPeriodicSwap2_2& getIterator(const RepPRVP& r)
	{
		return *new NSIteratorPeriodicSwap2_2(r);
	}

	virtual void print()
	{
		cout << "NSSeqPeriodicSwap2_2" << endl;
	}
};

#endif /*OPTFRAME_NSSEQPERIODICSWAP2_2_HPP_*/
